home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CICA 1993 April
/
CICA MS Windows - April 1993.iso
/
unzipped
/
programr
/
tp
/
custom
/
custom.pas
< prev
Wrap
Pascal/Delphi Source File
|
1991-07-24
|
24KB
|
847 lines
{************************************************************************
*
* Custom Controls Unit
*
* WRITTEN BY: Shawn Aubrey Baker (aka sab)
*
* COMPUSERVE ID: 76450,22
*
* CREDITS: This code started out being based on the work of
* Robert Norton, who uploaded a bitmap button unit to
* Compuserve. Thanks Robert, it helped a lot. Also, the
* code from the example unit (BITBTN.PAS) that came with
* Turbo Pascal for Windows was a big help. Thanks Borland.
*
* USE: As you wish. Please send any comments and/or bug fixes
* via mail to the above ID. IF IT DIES IT'S YOUR PROBLEM.
*
* NOTES: This file uses tabs = 3
*
* THE PROBLEM: The first time I tried this the custom bitmap buttons
* worked fine in a TWindow but died a horrible death in
* a TDlgWindow. This is because Windows creates the
* actual controls instead of OWL. OWL provides little
* (read NO) support for custom controls and assumes that
* any control from a resource is fully created by the time
* that OWL gets to its child window creation code. This
* means that OWL doesn't try to create the window (good!)
* but that it has installed set the window procedure to
* its standard initialization proc (bad!). This procedure
* (InitWndProc for those with the OWL source) depends on
* having a global variable (CreationWindow) set that points
* to the object being created. Since this variable hasn't
* been set the routine goes off into la-la land. Also, the
* InitResource method sets the DefaultProc pointer to nil,
* which again causes OWL to go astray.
*
* THE SOLUTION: The method I've used to get around this is to override
* the window procedure pointer in the TWndClass structure
* to point my own procedure (InitCustom). Unfortunately,
* the InitCustom procedure needs to get a pointer to the
* object being initialized in order to get the real window
* procedure out of the Instance variable. Therefore, I've
* had to create a collection of custom controls and get
* the pointers out of there. Again, this only applies to
* controls from resources, so the object is added to the
* collection in InitResource and removed from it in
* InitCustom. It is possible to get into problems with this
* if you Init 2 dialogs with custom controls before you
* ExecDialog either of them. If there is a TCustom control
* with the same ID in the two resources then there is no
* telling which one will get picked out of the collection.
* It's simple, DON'T DO THIS!!!
*
* As far as the TWindow.InitResource problem goes, I simply
* call TWindow.Init as it does and then set everything
* except the DefaultProc pointer in the same way as
* TWindow.InitResource does.
*
* THE END.
*
************************************************************************}
unit Custom;
interface
uses WinTypes,WinProcs,WObjects,Strings;
type
PCustom=^TCustom;
TCustom=object(TWindow)
constructor Init(AParent:PWindowsObject; AnId:integer;
ATitle:PChar; X,Y,W,H:integer);
constructor InitResource(AParent:PWindowsObject; AnID:word);
procedure SetupWindow; virtual;
function GetClassName:PChar; virtual;
procedure GetWindowClass(var AWndClass:TWndClass); virtual;
end;
PCustomButton=^TCustomButton;
TCustomButton=object(TCustom)
OwnMouse : boolean; { Is the mouse held captive? }
Pressed : boolean; { Is the button currently pressed? }
Default : boolean; { Is this the default button? }
constructor Init(AParent:PWindowsObject; AnID:integer;
AText:PChar; X,Y,W,H:integer; IsDefault:boolean);
constructor InitResource(AParent:PWindowsObject; AnID:integer);
procedure SetupWindow; virtual;
procedure WMMouseMove(var Msg:TMessage);
virtual wm_First + wm_MouseMove;
procedure WMLButtonDown(var Msg:TMessage);
virtual wm_First + wm_LButtonDown;
procedure WMLButtonUp(var Msg:TMessage);
virtual wm_First + wm_LButtonUp;
procedure WMSetFocus(var Msg:TMessage);
virtual wm_First + wm_SetFocus;
procedure WMKillFocus(var Msg:TMessage);
virtual wm_First + wm_KillFocus;
procedure WMKeyDown(var Msg:Tmessage);
virtual wm_First + wm_KeyDown;
procedure WMKeyUp(var Msg:Tmessage);
virtual wm_First + wm_KeyUp;
procedure WMGetDlgCode(var Msg:Tmessage);
virtual wm_First + wm_GetDlgCode;
procedure BMSetStyle(var Msg:Tmessage);
virtual wm_First + bm_SetStyle;
end;
PBitButton=^TBitButton;
TBitButton=object(TCustomButton)
UpBits : HBitMap;
FocUpBits : HBitMap;
DownBits : HBitMap;
UpName : PChar;
FocUpName : PChar;
DownName : PChar;
bmWidth : integer;
bmHeight : integer;
constructor Init(AParent:PWindowsObject; AnID,X,Y:integer;
AUpName,AFocUpName,ADownName:PChar;
IsDefault:boolean);
constructor InitResource(AParent:PWindowsObject; AnID:integer;
AUpName,AFocUpName,ADownName:PChar);
destructor Done; virtual;
procedure SetupWindow; virtual;
function GetClassName:PChar; virtual;
procedure Paint(DC:HDC; var PaintInfo:TPaintStruct); virtual;
end;
implementation
{------------------------------------------------------------------------
-------------------------------------------------------------------------
---- TCustom Object ----
-------------------------------------------------------------------------
------------------------------------------------------------------------}
{************************************************************************
*
* Name: InitCustom
*
* Purpose: Called with the first message for a custom control. This
* routine sets the window procedure to the one pointed to
* by Instance in the Custom object. The object is stored in
* the Customs collection by the Init/InitResource routine
* and it is removed here. This list is only needed in order
* to find the actual object.
*
* Parameters: Message - the first message (should be wm_NCCreate)
* WParam - more message info
* LParam - even more message info
*
* Return: window procedure return value (depends on the message command)
*
************************************************************************}
var Customs:PCollection; { collection of custom controls }
ACustom:PWindowsObject; { current custom control }
function InitCustom(HWindow:HWND; Message,WParam:word; LParam:longint):
longint; export;
var ID:longint;
{ finds the Custom object in the Customs collection }
function FindID(Custom:PCustom):boolean; far;
begin
FindID:=Custom^.GetID = ID;
end;
begin
{ find the Custom object, delete it from the collection }
ID:=GetWindowWord(HWindow,gww_ID);
ACustom:=Customs^.FirstThat(@FindID);
Customs^.Delete(ACustom);
{ set the window proc to the instance proc }
SetWindowLong(HWindow,gwl_WndProc,longint(ACustom^.Instance));
{ call the instance proc to handle the message }
asm
PUSH HWindow
PUSH Message
PUSH WParam
PUSH LParam.Word[2]
PUSH LParam.Word[0]
MOV AX,DS
LES DI,ACustom
CALL ES:[DI].TWindowsObject.Instance
end;
end;
{************************************************************************
*
* Name: TCustom.Init
*
* Purpose: Initializes a custom control.
*
* Parameters: AParent - parent window
* AnID - button ID
* ATitle - control title
* X,Y,W,H - position and size
*
* Return: None
*
************************************************************************}
constructor TCustom.Init(AParent:PWindowsObject; AnId:integer;
ATitle:PChar; X,Y,W,H:integer);
begin
TWindow.Init(AParent,ATitle);
Attr.Id:=AnId;
Attr.X:=X;
Attr.Y:=Y;
Attr.W:=W;
Attr.H:=H;
Attr.Style:=ws_Child or ws_Visible or ws_Group or ws_TabStop;
end;
{************************************************************************
*
* Name: TCustom.InitResource
*
* Purpose: Initializes a custom control from a resource and enables
* data transfer.
*
* Parameters: AParent - parent window
* AnID - button ID
*
* Return: None
*
************************************************************************}
constructor TCustom.InitResource(AParent:PWindowsObject; AnID:word);
begin
{ replacement code for TWindow.InitResource, needed }
{ because the TWindow routine sets DefaultProc to nil, }
{ wherease TWindow.Init sets it to the routine we want }
TWindow.Init(AParent,nil);
SetFlags(wb_FromResource,true);
FillChar(Attr,SizeOf(Attr),0);
Attr.ID:=AnID;
{ must pre-register because Windows creates controls from resources }
if not Register then Fail;
EnableTransfer;
{ add it to the Customs collection so that the InitCustom proc can find it }
Customs^.Insert(@self);
end;
{************************************************************************
*
* Name: TCustom.SetupWindow
*
* Purpose: Sets up the window and gets the attributes if the window
* is from a resource.
*
* Parameters: None
*
* Return: None
*
************************************************************************}
procedure TCustom.SetupWindow;
var Rect:TRect;
Pt:TPoint;
begin
TWindow.SetupWindow;
{ if it's from a resource then set the attributes }
if IsFlagSet(wb_FromResource) then
begin
{ get the client rect in screen co-ordinates }
GetWindowRect(HWindow,Rect);
Pt.X:=Rect.Left;
Pt.Y:=Rect.Top;
{ make the position relative to the parent window }
ScreenToClient(GetWindowWord(HWindow,gww_HWndParent),Pt);
Attr.X:=Pt.X;
Attr.Y:=Pt.Y;
{ get the client rect and set the window size }
GetClientRect(HWindow,Rect);
Attr.W:=Rect.Right-Rect.Left;
Attr.H:=Rect.Bottom-Rect.Top;
{ get the style info }
Attr.Style:=GetWindowWord(HWindow,gwl_Style);
Attr.ExStyle:=GetWindowWord(HWindow,gwl_ExStyle);
end;
end;
{************************************************************************
*
* Name: TCustom.GetClassName
*
* Purpose: Abstract virtual method that gets the class name for a
* custom control. Generates a run-time error to ensure
* that the descendants override the method with their
* own class name.
*
* Parameters: None
*
* Return: None
*
************************************************************************}
function TCustom.GetClassName:PChar;
begin
Abstract;
end;
{************************************************************************
*
* Name: TCustom.GetWindowClass
*
* Purpose: Sets the class info for a custom control. Overrides the
* TPW startup procedure to use code that will find the
* object in our "Customs" collection.
*
* Parameters: AWndClass - class information
*
* Return: None
*
************************************************************************}
procedure TCustom.GetWindowClass(var AWndClass:TWndClass);
begin
TWindow.GetWindowClass(AWndClass);
if IsFlagSet(wb_FromResource) then
AWndClass.lpfnWndProc:=@InitCustom;
end;
{------------------------------------------------------------------------
-------------------------------------------------------------------------
---- TCustomButton Object ----
-------------------------------------------------------------------------
------------------------------------------------------------------------}
{************************************************************************
*
* Name: TCustomButton.Init
*
* Purpose: Initializes a custom button.
*
* Parameters: AParent - parent window
* AnID - button ID
* AText - button text (or nil)
* X,Y,W,H - position and size
* IsDefault - default button ?
*
* Return: None
*
************************************************************************}
constructor TCustomButton.Init(AParent:PWindowsObject; AnID:integer;
AText:PChar; X,Y,W,H:integer;
IsDefault:boolean);
begin
TCustom.Init(AParent,AnID,nil,X,Y,10,10);
if IsDefault then
Attr.Style:=Attr.Style or bs_DefPushButton
else
Attr.Style:=Attr.Style or bs_PushButton;
end;
{************************************************************************
*
* Name: TCustomButton.InitResource
*
* Purpose: Initializes a custom button from a resource.
*
* Parameters: AParent - parent window
* AnID - button ID
*
* Return: None
*
************************************************************************}
constructor TCustomButton.InitResource(AParent:PWindowsObject; AnID:integer);
begin
TCustom.InitResource(AParent,AnID);
DisableTransfer;
end;
{************************************************************************
*
* Name: TCustomButton.SetupWindow
*
* Purpose: Sets up the window and initializes the state variables.
*
* Parameters: None
*
* Return: None
*
************************************************************************}
procedure TCustomButton.SetupWindow;
begin
TCustom.SetupWindow;
Pressed:=false;
OwnMouse:=false;
Default:=Attr.Style and bs_DefPushButton = bs_DefPushButton;
end;
{************************************************************************
*
* Name: TCustomButton.WMLButtonDown
*
* Purpose: repaint the button in the down position when the left
* mouse button is pressed.
*
* Parameters: Msg - a message
*
* Return: None
*
************************************************************************}
procedure TCustomButton.WMLButtonDown(var Msg:TMessage);
begin
{ if not already pressed then set state to pressed }
if not Pressed then
begin
if GetFocus <> hWindow then
SetFocus(hWindow);
Pressed:=true;
OwnMouse:=true;
SetCapture(hWindow);
end;
{ trigger repaint }
InvalidateRect(hWindow,nil,false);
end;
{************************************************************************
*
* Name: TCustomButton.WMLButtonUp
*
* Purpose: If the left mouse button is pressed and then released
* over the button then repaint it as unpressed and notify
* the parent window.
*
* Parameters: Msg - a message
*
* Return: None
*
************************************************************************}
procedure TCustomButton.WMLButtonUp(var Msg:TMessage);
begin
if OwnMouse then
begin
ReleaseCapture;
OwnMouse:=false;
if Pressed then { trigger repaint and notify parent }
begin
Pressed:=false;
InvalidateRect(hWindow,nil,false);
PostMessage(Parent^.hWindow,wm_Command,Attr.Id,longint(hWindow));
end;
end;
end;
{************************************************************************
*
* Name: TCustomButton.WMMouseMove
*
* Purpose: Repaints the button when the mouse is pressed and moves
* into and outof the button window.
*
* Parameters: Msg - a message
*
* Return: None
*
************************************************************************}
procedure TCustomButton.WMMouseMove(var Msg:TMessage);
var BtnRect:TRect;
MousePt:TPoint;
begin
{ get window rectangle and mouse point }
GetClientRect(hWindow,BtnRect);
MousePt.X:=integer(Msg.lParamLo);
MousePt.Y:=integer(Msg.lParamHi);
{ if the mouse is over the button }
if PtInRect(BtnRect,MousePt) then
begin
{ if the mouse is moved into the button area }
if OwnMouse and (not Pressed) then
begin
Pressed:=true;
InvalidateRect(hWindow,nil,false);
end;
end
{ if the mouse is moved out of the button area }
else if Pressed then
begin
Pressed:=false;
InvalidateRect(hWindow,nil,false);
end;
end;
{************************************************************************
*
* Name: TCustomButton.WMSetFocus
*
* Purpose: Forces repaint if the focus is set to the button.
*
* Parameters: Msg - a message
*
* Return: None
*
************************************************************************}
procedure TCustomButton.WMSetFocus(var Msg:TMessage);
begin
InvalidateRect(hWindow,nil,false);
end;
{************************************************************************
*
* Name: TCustomButton.WMKillFocus
*
* Purpose: Forces repaint if the focus is taken away from the button.
*
* Parameters: Msg - a message
*
* Return: None
*
************************************************************************}
procedure TCustomButton.WMKillFocus(var Msg:TMessage);
begin
InvalidateRect(hWindow,nil,false);
end;
{************************************************************************
*
* Name: TCustomButton.WMKeyDown
*
* Purpose: Repaints the button in the down position if the space
* bar is pressed on the button.
*
* Parameters: Msg - a message
*
* Return: None
*
************************************************************************}
procedure TCustomButton.WMKeyDown(var Msg:Tmessage);
begin
if (Msg.wParam = $20) and not Pressed and not OwnMouse then
begin
Pressed:=true;
InvalidateRect(hWindow,nil,false);
end;
end;
{************************************************************************
*
* Name: TCustomButton.WMKeyUp
*
* Purpose: Repaints the button in the up position and notifies the
* parent window if the space bar is pressed on the button.
*
* Parameters: Msg - a message
*
* Return: None
*
************************************************************************}
procedure TCustomButton.WMKeyUP(var Msg:Tmessage);
begin
if (Msg.wParam = $20) and Pressed and not OwnMouse then
begin
Pressed:=false;
InvalidateRect(hWindow,nil,false);
PostMessage(Parent^.hWindow,wm_Command,Attr.Id,longint(hWindow));
end;
end;
{************************************************************************
*
* Name: TCustomButton.WMGetDlgCode
*
* Purpose: Gets whether or not the button is the default.
*
* Parameters: Msg - a message
*
* Return: None
*
************************************************************************}
procedure TCustomButton.WMGetDlgCode(var Msg:Tmessage);
begin
if Default then
Msg.Result:=dlgc_DefPushButton
else
Msg.Result:=dlgc_UndefPushButton;
end;
{************************************************************************
*
* Name: TCustomButton.BMSetStyle
*
* Purpose: Sets the button style to either default or not.
*
* Parameters: Msg - a message
*
* Return: None
*
************************************************************************}
procedure TCustomButton.BMSetStyle(var Msg:Tmessage);
var OldDefault:boolean;
begin
OldDefault:=Default;
Default:=Msg.WParam = bs_DefPushButton;
if Default <> OldDefault then
InvalidateRect(hWindow,nil,false);
end;
{------------------------------------------------------------------------
-------------------------------------------------------------------------
---- TBitButton Object ----
-------------------------------------------------------------------------
------------------------------------------------------------------------}
{************************************************************************
*
* Name: TBitButton.Init
*
* Purpose: Initializes a button.
*
* Parameters: AParent - parent window
* AnID - button ID
* X,Y - position
* IsDefault - default button ?
* AUpName - name of resource for up bitmap
* AFocUpName - name of resource for up bitmap when focused
* ADownName - name of resource for down bitmap
*
* Return: None
*
************************************************************************}
constructor TBitButton.Init(AParent:PWindowsObject; AnID,X,Y:integer;
AUpName,AFocUpName,ADownName:PChar;
IsDefault:boolean);
begin
TCustomButton.Init(AParent,AnID,nil,X,Y,10,10,IsDefault);
UpName:=AUpName;
FocUpName:=AFocUpName;
DownName:=ADownName;
end;
{************************************************************************
*
* Name: TBitButton.InitResource
*
* Purpose: Initializes a button from a resource.
*
* Parameters: AParent - parent window
* AnID - button ID
* AUpName - name of resource for up bitmap
* AFocUpName - name of resource for up bitmap when focused
* ADownName - name of resource for down bitmap
*
* Return: None
*
************************************************************************}
constructor TBitButton.InitResource(AParent:PWindowsObject; AnID:integer;
AUpName,AFocUpName,ADownName:PChar);
begin
TCustomButton.InitResource(AParent,AnID);
UpName:=AUpName;
FocUpName:=AFocUpName;
DownName:=ADownName;
end;
{************************************************************************
*
* Name: TBitButton.Done
*
* Purpose: Destroys the button.
*
* Parameters: None
*
* Return: None
*
************************************************************************}
destructor TBitButton.Done;
begin
DeleteObject(UpBits);
DeleteObject(FocUpBits);
DeleteObject(DownBits);
TCustomButton.Done;
end;
{************************************************************************
*
* Name: TBitButton.SetupWindow
*
* Purpose: Loads the bitmaps for a button, resizes the window
* accordingly, and initializes the state variables.
*
* Parameters: None
*
* Return: None
*
************************************************************************}
procedure TBitButton.SetupWindow;
var bm:TBitMap;
begin
TCustomButton.SetupWindow;
{ load the bitmaps }
UpBits:=LoadBitmap(hInstance,UpName);
FocUpBits:=LoadBitmap(hInstance,FocUpName);
DownBits:=LoadBitmap(hInstance,DownName);
{ resize the window to fit the bitmaps }
GetObject(DownBits,SizeOf(bm),@bm);
MoveWindow(HWindow,Attr.X,Attr.Y,bm.bmWidth+2,bm.bmHeight+2,false);
bmWidth:=bm.bmWidth;
bmHeight:=bm.bmHeight;
end;
{************************************************************************
*
* Name: TBitButton.GetClassName
*
* Purpose: Gets the class name for a bitmap button.
*
* Parameters: None
*
* Return: pointer to the class name
*
************************************************************************}
function TBitButton.GetClassName;
begin
GetClassName:='BitButton';
end;
{************************************************************************
*
* Name: TBitButton.Paint
*
* Purpose: Paints one of the bitmaps into the window depending on
* the current state.
*
* Parameters: DC - device context to paint into
* PaintInfo - painting information
*
* Return: None
*
************************************************************************}
procedure TBitButton.Paint(DC:HDC; var PaintInfo:TPaintStruct);
var BitsDC:HDC;
OldBitmap:HBitMap;
OldBrush:HBrush;
begin
{ draw the border }
if Default then
OldBrush:=SelectObject(DC,GetStockObject(Black_Brush))
else
OldBrush:=SelectObject(DC,GetStockObject(White_Brush));
PatBlt(DC,0,0,Attr.W,1,PatCopy);
PatBlt(DC,0,0,1,Attr.H,PatCopy);
PatBlt(DC,0,Attr.H-1,Attr.W,1,PatCopy);
PatBlt(DC,Attr.W-1,0,1,Attr.H,PatCopy);
SelectObject(DC,OldBrush);
{ draw the button }
BitsDC:=CreateCompatibleDC(DC);
if Pressed then
OldBitmap:=SelectObject(BitsDC,DownBits)
else if GetFocus = hWindow then
OldBitmap:=SelectObject(BitsDC,FocUpBits)
else
OldBitmap:=SelectObject(BitsDC,UpBits);
BitBlt(DC,1,1,bmWidth,bmHeight,BitsDC,0,0,SrcCopy);
SelectObject(BitsDC,OldBitmap);
DeleteDC(BitsDC);
end;
{------------------------------------------------------------------------
-------------------------------------------------------------------------
---- Unit initialization ----
-------------------------------------------------------------------------
------------------------------------------------------------------------}
begin
New(Customs,Init(40,10));
end.